package heap

import (
	"JVM/Jvmgo/GoToJVM/classfile"
	"JVM/Jvmgo/GoToJVM/classpath"
	"fmt"
)

/*
使用map来存储已经加载过的类，其中key为类的全限定名，*Class为类指针
*/
type ClassLoader struct {
	cp          *classpath.Classpath
	verboseFlag bool
	classMap    map[string]*Class // loaded classes
}

func NewClassLoader(cp *classpath.Classpath, verboseFlag bool) *ClassLoader {
	loader := &ClassLoader{
		cp:          cp,
		verboseFlag: verboseFlag,
		classMap:    make(map[string]*Class),
	}

	loader.loadBasicClasses()
	loader.loadPrimitiveClasses()
	return loader
}

func (self *ClassLoader) loadPrimitiveClasses() {
	for primitiveType := range primitiveTypes {
		self.loadPrimitiveClass(primitiveType)
	}
}

func (self *ClassLoader) loadPrimitiveClass(className string) {
	class := &Class{
		accessFlags: ACC_PUBLIC, // todo
		name:        className,
		loader:      self,
		initStarted: true,
	}
	class.jClass = self.classMap["java/lang/Class"].NewObject()
	class.jClass.extra = class
	self.classMap[className] = class
}

/*
通过 loadBasicClasses和LoadClass可以将所有加载到方法区的类都提供了类对象。（注：方法区存储了类信息）
*/

// 新建对象就为其创建类对象指针
func (self *ClassLoader) loadBasicClasses() {
	// 加载Class类会为其父类和接口进行加载
	jlClassClass := self.LoadClass("java/lang/Class")
	// 遍历classMap 给已经加载每一个类关联对象
	for _, class := range self.classMap {
		if class.jClass == nil {
			// 类对象创建
			class.jClass = jlClassClass.NewObject()
			// extra 存储类指针
			class.jClass.extra = class
		}
	}
}

func (self *ClassLoader) LoadClass(name string) *Class {
	if class, ok := self.classMap[name]; ok {
		// 已经存储了，直接返回类指针
		return class
	}

	var class *Class
	if name[0] == '[' {
		class = self.loadArrayClass(name)
	} else {
		class = self.loadNonArrayClass(name)
	}

	// 加载类就为其创建类对象指针
	// 查看 "java/lang/Class" 是否被加载，如果否就新建对象
	if jlClassClass, ok := self.classMap["java/lang/Class"]; ok {
		class.jClass = jlClassClass.NewObject()
		class.jClass.extra = class
	}

	return class
}

func (self *ClassLoader) loadArrayClass(name string) *Class {
	class := &Class{
		accessFlags: ACC_PUBLIC, // todo
		name:        name,
		loader:      self,
		initStarted: true,                               // 不需要初始化
		superClass:  self.LoadClass("java/lang/Object"), // 数组类超类是Object
		interfaces: []*Class{
			// 实现两个接口类 Cloneable, Serializable
			self.LoadClass("java/lang/Cloneable"),
			self.LoadClass("java/io/Serializable"),
		},
	}
	self.classMap[name] = class
	return class
}

func (self *ClassLoader) loadNonArrayClass(name string) *Class {
	// 1. 找到class文件读取到内存中（加载）
	data, entry := self.readClass(name)
	// 2. 解析class文件，生成虚拟机可用的类数据，放入方法区
	class := self.defineClass(data)
	// 3. 进行链接
	link(class)
	// 判断是否为被允许显示
	if self.verboseFlag {
		fmt.Printf("[Loaded %s from %s]\n", name, entry)
	}
	return class
}

func (self *ClassLoader) readClass(name string) ([]byte, classpath.Entry) {
	data, entry, err := self.cp.ReadClass(name)
	if err != nil {
		panic("java.lang.ClassNotFoundException: " + name) // 字符串拼接
	}
	return data, entry
}

func (self *ClassLoader) defineClass(data []byte) *Class {
	class := parseClass(data)
	class.loader = self
	resolveSuperClass(class)
	resolveInterfaces(class)
	self.classMap[class.name] = class
	return class
}

func parseClass(data []byte) *Class {
	cf, err := classfile.Parse(data)
	if err != nil {
		panic("java.lang.ClassFormatError")
	}
	return newClass(cf)
}

func resolveSuperClass(class *Class) {
	if class.name != "java/lang/Object" {
		class.superClass = class.loader.LoadClass(class.superClassName)
		if class.superClass.IsFinal() {
			panic("java.lang.Illegal extend!")
		}
	}
}

func resolveInterfaces(class *Class) {
	interfaceCount := len(class.interfaceNames)
	if interfaceCount > 0 {
		class.interfaces = make([]*Class, interfaceCount)
		for i, interfaceName := range class.interfaceNames {
			class.interfaces[i] = class.loader.LoadClass(interfaceName)
		}
	}
}

func link(class *Class) {
	verify(class)
	prepare(class)
}

/*
由于我们需要验证文件来源是否为可信编译器编译而来，所以需要进行校验
这里只做了部分校验
*/
func verify(class *Class) {
	// 文件格式验证
	// 1. 先验证魔数是否为 0xCAFEBABE
	if !(class.magic == 0xCAFEBABE) {
		panic("java.lang.ClassFormatError: magic!")
	}
	// 版本号验证，判断版本号是否在Java虚拟机接受范围内
	// 由于45 -> 49 存在副版本号，再者这几个版本jdk早已退环境，就不在支持
	if !(class.majorVersion > 50 && class.majorVersion < 65) {
		panic("java.lang.ClassFormatError: Illegal version number!")
	}
	// 2. 检查常量池里面是否有不支持类型
	// (即检查tag，由于我们的constant pool 存放了所有常量变量，遍历读取u2即可)
	// 由于我们的使用常量池构造器新建一个指针对象，直接调用了常量池的解析函数，所以忽略对常量池数据的校验

	// 3. 校验访问修饰符
	/*
			由于在class文件中的access flag 是叠加的，
			例如在我们的class文件中，access flag是33，转化为16进制为0x0021,
			即可拆为0x0020 和 0x0001 即为public super
			(这里解释为什么有super，因为super是是否允许调用invokespecial字节码指令的新语义，
		所以在jdk 1.0.2版本之后的所有类中都应该被设置)
	*/
	af := class.accessFlags
	// 在加载完Object类后再对super取&
	// 普通类(非接口)
	if (af & ACC_INTERFACE) == 0 {
		if (af&ACC_FINAL) != 0 && (af&ACC_ABSTRACT) != 0 {
			panic("java.lang.ClassFormatError: A class cannot be both final and abstract!")
		}

		if (af&ACC_ANNOTATION) != 0 || (af&ACC_ENUM) != 0 {
			panic("java.lang.ClassFormatError: A regular class cannot be an annotation or an enum!")
		}
	}
	if (af & ACC_INTERFACE) != 0 {
		if (af&ACC_FINAL) != 0 || (af&ACC_SUPER) != 0 || (af&ACC_ENUM) != 0 {
			panic("java.lang.ClassFormatError: An interface cannot be final, super, or an enum!")
		}

		if (af & ACC_ABSTRACT) == 0 {
			panic("java.lang.ClassFormatError: An interface must be abstract!")
		}
	}
	if (af & ACC_ANNOTATION) != 0 {
		if (af&ACC_FINAL) != 0 || (af&ACC_SUPER) != 0 || (af&ACC_ENUM) != 0 {
			panic("java.lang.ClassFormatError: An annotation cannot be final, super, or an enum!")
		}

		if (af&ACC_INTERFACE) == 0 || (af&ACC_ABSTRACT) == 0 {
			panic("java.lang.ClassFormatError: An annotation must be an interface and abstract!")
		}
	}
	if (af & ACC_ENUM) != 0 {
		if (af&ACC_ABSTRACT) != 0 || (af&ACC_INTERFACE) != 0 || (af&ACC_ANNOTATION) != 0 {
			panic("java.lang.ClassFormatError: An enum cannot be abstract, an interface, or an annotation!")
		}
	}

	// todo 字段和方法校验
}

func prepare(class *Class) {
	calcInstanceFieldSlotIds(class)
	calcStaticFieldSlotIds(class)
	allocAndInitStaticVars(class)
}

// 记录普通字段个数
func calcInstanceFieldSlotIds(class *Class) {
	slotId := uint(0)
	if class.superClass != nil {
		slotId = class.superClass.instanceSlotCount
	}
	for _, field := range class.fields {
		if !field.IsStatic() {
			field.slotId = slotId
			slotId++
			if field.isLongOrDouble() {
				slotId++
			}
		}
	}
	class.instanceSlotCount = slotId
}

// 记录静态字段个数
func calcStaticFieldSlotIds(class *Class) {
	slotId := uint(0)
	for _, field := range class.fields {
		if field.IsStatic() {
			field.slotId = slotId
			slotId++
			if field.isLongOrDouble() {
				slotId++
			}
		}
	}
	class.staticSlotCount = slotId
}

// 为类变量分配空间，赋初值
func allocAndInitStaticVars(class *Class) {
	class.staticVars = newSlots(class.staticSlotCount)
	for _, field := range class.fields {
		if field.IsStatic() && field.IsFinal() {
			initStaticFinalVar(class, field)
		}
	}
}

// 静态final类型值赋值
func initStaticFinalVar(class *Class, field *Field) {
	vars := class.staticVars
	cp := class.constantPool
	cpIndex := field.ConstValueIndex()
	slotId := field.SlotId()

	if cpIndex > 0 {
		switch field.Descriptor() {
		case "Z", "B", "C", "S", "I":
			val := cp.GetConstant(cpIndex).(int32)
			vars.SetInt(slotId, val)
		case "J":
			val := cp.GetConstant(cpIndex).(int64)
			vars.SetLong(slotId, val)
		case "F":
			val := cp.GetConstant(cpIndex).(float32)
			vars.SetFloat(slotId, val)
		case "D":
			val := cp.GetConstant(cpIndex).(float64)
			//fmt.Printf("the type of var is: %v,\n the val is:", reflect.TypeOf(val))
			vars.SetDouble(slotId, val)
		case "Ljava/lang/String;":
			goStr := cp.GetConstant(cpIndex).(string)
			jStr := JString(class.Loader(), goStr)
			vars.SetRef(slotId, jStr)
		}
	}
}
